
#ifndef Velyn_Slice_h
#define Velyn_Slice_h

// slice method
// http://stackoverflow.com/questions/7168484/line-segment-and-plane-intersection-test

#include <stdlib.h>   
#include <stdio.h> 

#include "IDisplacement.h"

namespace Velyn {
    
    namespace Displacements {

        /*
         * Slices the object by creating a random plane and projecting all the vertices
         * that intersect onto the plane.
         */

        class Slice : public IDisplacement {
            
        private:
            
            /*
             * This seed value determines the plane created, this makes sure that each
             * seed is different to produce a different plane.
             */
            
            static int seedValue;
            
            /*
             * Gives a random value between a low and high value.
             */
            
            float generateRandomFloatValue(float lowValue, float highValue) {
                
                // get a seed using the time
                srand((unsigned int)time(NULL));
                
                // generate a seed value
                seedValue += seedValue * rand() + 37;
                
                // seed a random number 
                srand(seedValue);
                
                // cout << "Seed Value: " << seedValue << endl;
                
                // return a random value
                return ((highValue - lowValue) * ((float)rand() / RAND_MAX)) + lowValue;
            }
            
        public:
            
            Slice() {}
            
            ~Slice() {}
            
            /*
             * Displaces the object with the slice operation.
             */
            
            virtual int displace(TriangleMesh* mesh) {
                
                // the plane normal
                Vector3 planeNormal;
                
                // the plane
                Vector4 plane;
                
                // get the vertices
                Vertex* vertices = mesh->getVertices();
                
                // get the normals
                Normal* normals = mesh->getNormals();
                
                // randomize a plane normal
                float x = generateRandomFloatValue(-1.0f, 1.0f);
                float y = generateRandomFloatValue(-1.0f, 1.0f);
                float z = generateRandomFloatValue(-1.0f, 1.0f);
                
                // cout << "Plane Normal Values: " << x << " " << y << " " << z << endl;
                
                // set the values of the random plane normal
                planeNormal.setValue(x, y, z);
                
                // cout << "Plane Normal: ";
                // planeNormal.print();
                
                // normalize the plane
                planeNormal.normalize();
                
                // float planeDistance = generateRandomFloatValue(0.3f, 6.0f);
                
                // set the plane
                plane.setValue(planeNormal.x, planeNormal.y, planeNormal.z, 0.5f);
                // plane.setValue(planeNormal.x, planeNormal.y, planeNormal.z, planeDistance);
            
                // cout << "Plane Values: ";
                // plane.print();
                
                // for each vertex in the mesh
                for(int vertex = 0; vertex < mesh->numberVertices(); vertex++) {
                    
                    // Vector4 pointVec4(point.x, point.y, point.z, 0.0f);
                    // Vector4 point(vertices[vertex].x, vertices[vertex].y, vertices[vertex].z, 0.0f);
                    
                    // Vector3 pointVec3 = vertices[vertex];
                    
                    // get the distance from the point
                    // float distance = plane.dot(Vector4(point.x, point.y, point.z, 0.0f));
                    float distance = plane.dot(Vector4(vertices[vertex].x, vertices[vertex].y, vertices[vertex].z, 0.0f));
                    
                    // cout << "Distance: " << distance << endl;
                    
                    // check if it intersects
                    if(distance > plane.w and distance > 0.0f) {
                        
                        // cout << " -- CUTS -- " << endl;
                        
                        // calculate the plane???? (not sure what this does)
                        float d = planeNormal.dot(Vector3(plane.w / plane.x, 0.0f, 0.0f));
                        
                        // calculate a ray from the point to the origin
                        // Vector3 ray = pointVec3 - origin;
                        Vector3 ray = normals[vertex];
                        
                        // the origin
                        Vector3 origin(0.0f, 0.0f, 0.0f);
                        
                        // get the t value for the directed line ray intersecting the plane
                        float t = ((d - planeNormal.dot(origin)) / planeNormal.dot(ray));
                        
                        // scale the ray by t
                        Vector3 newRay = ray * t;
                        
                        // calculate the contact point
                        // Vector3 intersectionOnPlane = Vector3(0.0f, 0.0f, 0.0f) + newRay;
                        Vector3 intersectionOnPlane = origin + newRay;
                        
                        // set the point to where it intersects on the plane
                        // point = intersectionOnPlane;
                        // vertices[vertex].setValue(intersectionOnPlane.x, intersectionOnPlane.y, intersectionOnPlane.z);
                        vertices[vertex].setValue(intersectionOnPlane.getValue());
                        
                        // vertices[vertex] = intersectionOnPlane;
                        
                        // cout << "Point New Value: ";
                        // point.print();
                        
                    } else {
                        // cout << " -- DOES NOT CUT -- " << endl;
                        
                        // cout << "Point Value: ";
                        // point.print();
                    }
                    
                }
                
                return seedValue;
                
            }
        
        };
        
        int Slice::seedValue = 0;
        
    }
}

#endif
